package com.sprucetec.live.utils;

import com.dyuproject.protostuff.LinkedBuffer;
import com.dyuproject.protostuff.ProtostuffIOUtil;
import com.dyuproject.protostuff.Schema;
import com.dyuproject.protostuff.runtime.RuntimeSchema;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.SerializationException;

/**
 * @Author Wangshaobo
 * @Description protostuff 序列化
 * @Date 2019/1/13 11:05
 */
public class ProtostuffSerializer implements RedisSerializer<Object> {

    private boolean isEmpty(byte[] data) {
        return (data == null || data.length == 0);
    }

    private final Schema<ProtoWrapper> schema;

    private final ProtoWrapper wrapper;

    /**
     * 避免每次序列化都重新申请Buffer空间, 且多线程环境下，buffer.clear()执行顺序
     */
    private ThreadLocal<LinkedBuffer> buffer = ThreadLocal.withInitial(() -> LinkedBuffer.allocate(LinkedBuffer.DEFAULT_BUFFER_SIZE));

    public ProtostuffSerializer() {
        this.wrapper = new ProtoWrapper();
        this.schema = RuntimeSchema.getSchema(ProtoWrapper.class);
    }

    @Override
    public byte[] serialize(Object t) throws SerializationException {
        if (t == null) {
            return new byte[0];
        }
        LinkedBuffer buf = buffer.get();
        wrapper.data = t;
        try {
            return ProtostuffIOUtil.toByteArray(wrapper, schema, buf);
        } finally {
            buf.clear();
        }
    }

    @Override
    public Object deserialize(byte[] bytes) throws SerializationException {
        if (isEmpty(bytes)) {
            return null;
        }
        try {
            ProtoWrapper newMessage = schema.newMessage();
            ProtostuffIOUtil.mergeFrom(bytes, newMessage, schema);
            return newMessage.data;
        } catch (Exception ex) {
            return null;
        }
    }

    private static class ProtoWrapper {

        public Object data;

    }
}